/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import path from 'path';

import { ModulePathInfoIml } from '../../common/iml/module-path-info-iml.js';
import { BuildArtifactExtension } from '../../const/build-directory-const.js';
import { CommonConst, DefaultTargetConst } from '../../const/common-const.js';
import { DeviceTypeConst } from '../../const/device-type-const.js';
import { RuntimeTypeEnum } from '../../enum/runtime-type-enum.js';
import { LegacyAbilityModel } from '../../model/ability/legacy-ability-model.js';
import { LegacyModuleModelImpl } from '../../model/module/legacy-module-model-impl.js';
import { ModuleModel } from '../../model/module/module-model.js';
import { SourceSetModel } from '../../model/source-set/source-set-model.js';
import { ModuleBuildProfile } from '../../options/build/module-build-profile.js';
import { ProjectBuildProfile } from '../../options/build/project-build-profile.js';
import { AbstractModulePlugin } from '../../plugin/common/abstract-module-plugin.js';
import { OhosPluginId } from '../../plugin/common/ohos-plugin-id.js';
import { HapPlugin } from '../../plugin/hap-plugin.js';
import { HspPlugin } from '../../plugin/hsp-plugin.js';
import { ApiType } from '../../project/data/hap-extra-info.js';
import { getElementFromArr } from '../../utils/array-util.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { Status } from '../service/status.js';
import AbilityBuildOpt = ModuleBuildProfile.AbilityBuildOpt;
import ProductApiMeta = ProjectBuildProfile.ProductApiMeta;
import fse from 'fs-extra';

import { getJson5Obj } from '../../utils/json-util.js';
import { ModuleTaskService } from '../service/module-task-service.js';
import { ApiVersion } from '../../const/sdk-const.js';

/**
 * 针对每一个Module中的target初始化的一个数据对象
 *
 * @since 2021/12/24
 */
export class ModuleTargetData {
  private readonly _moduleModel: ModuleModel;
  private readonly _target: ModuleBuildProfile.ModuleTargetBuildOpt;
  private readonly _pathInfo: ModulePathInfoIml;
  private readonly _product: ProjectBuildProfile.ProductBuildOpt;
  private readonly _targetStatus: Status;
  private readonly _moduleSourceSetModel: SourceSetModel;
  private readonly compileApiVersion: number;
  private readonly compatibleApiVersion: number;
  private readonly compatibleSdkVersionStage: string | undefined;
  private readonly targetSdkVersion: number | undefined;
  private readonly _apiMeta: ProductApiMeta;
  private _log: OhosLogger = OhosLogger.getLogger('Target');

  constructor(
    moduleModel: ModuleModel,
    target: ModuleBuildProfile.ModuleTargetBuildOpt,
    pathInfo: ModulePathInfoIml,
    product: ProjectBuildProfile.ProductBuildOpt,
  ) {
    this.compileApiVersion = moduleModel.getCompileApiMetaByProduct(product.name).version;
    this.compatibleApiVersion = moduleModel.getCompatibleApiMetaByProduct(product.name).version;
    this.targetSdkVersion = moduleModel.getTargetApiMetaByProduct(product.name)?.version;

    // compatibleSdkVersionStage目前只有在API12中起效
    if (this.compatibleApiVersion === ApiVersion.API_VERSION_12) {
      this.compatibleSdkVersionStage = product.compatibleSdkVersionStage || 'beta1';
    }
    const productApiMeta: ProjectBuildProfile.ProductApiMeta | undefined = moduleModel.getProductApiMeta(product.name);
    if (!productApiMeta) {
      throw new Error(`Can not find product ${product}.`);
    }
    this._apiMeta = productApiMeta;
    this._moduleModel = moduleModel;
    this._target = target;
    this._pathInfo = pathInfo;
    this._product = product;

    if (product.runtimeOS) {
      this._targetStatus = new Status(product);
    } else {
      if (!target.runtimeOS) {
        this._targetStatus = new Status({ runtimeOS: 'OpenHarmony' });
      }
      this._targetStatus = new Status(target);
    }
    this._moduleSourceSetModel = this._moduleModel.getSourceSetByTargetName(target.name);
  }

  isHarmonyOS(): boolean {
    return this._targetStatus?.is(RuntimeTypeEnum.HARMONY_OS) ?? false;
  }

  getApiMeta() {
    return this._apiMeta;
  }

  getCompileApiVersion() {
    return this.compileApiVersion;
  }

  getCompatibleApiVersion() {
    return this.compatibleApiVersion;
  }

  getTargetSdkVersion() {
    return this.targetSdkVersion;
  }

  getCompatibleSdkVersionStage(): string | undefined {
    return this.compatibleSdkVersionStage;
  }

  /**
   * 返回该target对应的模块模型
   */
  getModuleModel(): ModuleModel {
    return this._moduleModel;
  }

  getModuleSourceSetModel(): SourceSetModel {
    return this._moduleSourceSetModel;
  }

  /**
   * 返回该target对应的模块关联的entry模块
   */
  getRelatedEntryModules(): string[] | undefined {
    return this.getModuleModel().getRelatedEntryModules();
  }

  getTargetName(): string {
    return this._target.name;
  }

  getTargetOpt(): ModuleBuildProfile.ModuleTargetBuildOpt {
    return this._target;
  }

  getPathInfo(): ModulePathInfoIml {
    return this._pathInfo;
  }

  /**
   * 根据入参获取hap包的名字, 格式为: moduleName(-entryName)-targetName(-signed).hap
   * 入参是null时, 不带是否签名
   *
   * @param {string} entryName 模块可能关联的entry名
   * @param {boolean | null} isSigned 是否签名
   * @param suffix 打出包的包格式,默认是.hap
   * @param deviceType 富瘦设备类型
   * @param targetName 当前target名
   * @returns {string}
   */
  getModuleTargetOutputFileName(
    entryName: string,
    isSigned: boolean | null,
    suffix = BuildArtifactExtension.DOT_HAP,
    deviceType?: string,
    targetName?: string,
  ): string {
    const moduleName = this._moduleModel.getName();
    const moduleType = this._moduleModel.getModuleType();
    const relatedEntryModules = this._moduleModel.getRelatedEntryModules();
    const noRelatedEntry = entryName === '' || !ModuleTaskService.checkEntryModules(moduleType, relatedEntryModules);
    const entryMark = noRelatedEntry ? '' : `-${entryName}`;
    const outputTargetName = targetName ?? this._target.name;
    let deviceTypeSuffix = '';
    if (deviceType === DeviceTypeConst.LITE) {
      deviceTypeSuffix = `-${deviceType}`;
    }
    if (isSigned === null) {
      return `${moduleName}${entryMark}-${outputTargetName}${deviceTypeSuffix}${suffix}`;
    }
    const signedSuffix = isSigned ? 'signed' : 'unsigned';
    const artifactName = this._target.output?.artifactName;
    return artifactName
      ? `${artifactName}${isSigned ? '' : '-unsigned'}${suffix}`
      : `${moduleName}${entryMark}-${outputTargetName}${deviceTypeSuffix}-${signedSuffix}${suffix}`;
  }

  /**
   * 获取本模块的apk名称
   * 注意: 只在fa模型中调用该方法
   *
   * @param {boolean} isCut 是否是cutApk
   * @returns {string}
   */
  getApkName(isCut = false): string {
    const moduleType = (this._moduleModel as LegacyModuleModelImpl).getModuleType(this.getTargetName());
    if (isCut) {
      return `${this._moduleModel.getName()}-${this._target.name}-cut-${moduleType}.apk`;
    }
    return `${this._moduleModel.getName()}-${this._target.name}-${moduleType}.apk`;
  }

  getTargetStatus(): Status {
    return this._targetStatus!;
  }

  getProduct(): ProjectBuildProfile.ProductBuildOpt {
    return this._product;
  }

  /**
   * 根据name寻找其他模块的targetData
   * 若没有该模块, 或者该模块下没有同名的targetName, 则返回undefined
   *
   * @param {string} name
   * @param {string} target
   * @returns {ModuleTargetData | undefined}
   */
  findTargetDataByName(name: string, target?: string): ModuleTargetData | undefined {
    const module = this._moduleModel.getModule().findModuleByName(name);
    if (module === undefined) {
      return undefined;
    }
    let plugin: AbstractModulePlugin = module.getPluginById(OhosPluginId.OHOS_HAP_PLUGIN) as HapPlugin;
    if (plugin === undefined) {
      plugin = module.getPluginById(OhosPluginId.OHOS_HSP_PLUGIN) as HspPlugin;
    }

    // require the same target name
    return plugin.getTaskService()?.findTargetDataByName(target ? target : this.getTargetName());
  }

  /**
   * 根据所关联entry模块name查找target
   * 存在同名则返回同名target 否则返回default
   *
   * @param entryName 与feature模块关联的entry模块Name
   */
  findRelatedTargetData(entryName: string): ModuleTargetData | undefined {
    const module = this._moduleModel.getModule().findModuleByName(entryName);
    if (module === undefined || this._moduleModel.isHspModule()) {
      return undefined;
    }
    const plugin = module.getPluginById(OhosPluginId.OHOS_HAP_PLUGIN) as HapPlugin;
    const sameNameTargetInEntry = plugin.getTaskService()?.findTargetDataByName(this.getTargetName());
    return sameNameTargetInEntry ?? plugin.getTaskService()?.findTargetDataByName(DefaultTargetConst.DEFAULT_TARGET);
  }

  /**
   * 根据config.json中的抽象的ability对象, 寻找target中对应的配置
   *
   * @param {LegacyAbilityModel} abilityObj
   * @returns {ModuleBuildProfile.AbilityBuildOpt | undefined}
   */
  findTargetConfigOpt(abilityObj: LegacyAbilityModel): AbilityBuildOpt | undefined {
    return getElementFromArr(this.getTargetOpt().source?.abilities, abilityObj.getName());
  }

  /**
   * 获取target中配置的设备类型,
   * 若没有配置, 或配置为空的数组, 返回配置文件中的deviceType
   * 若target中的设备类型不是模块的子集, 则报错
   *
   * @returns {string[]}
   */
  getTargetDeviceType(): string[] {
    const targetDeviceType = this.getTargetOpt().config?.deviceType;
    if (targetDeviceType === undefined || targetDeviceType.length === 0) {
      return this._moduleModel.getDeviceTypes();
    }
    if (!targetDeviceType.every((deviceType) => this._moduleModel.getDeviceTypes().includes(deviceType))) {
      OhosLogger.getLogger('target')
        .printErrorExit(
          'INVALID_TARGET_DEVICETYPE',
          [this._moduleModel.getProfilePath()],
          [[this.getTargetName()]]
        );
    }
    return targetDeviceType;
  }

  /**
   * 判断是否包含富设备
   */
  public hasRichDeviceInTarget(): boolean {
    return this.getTargetDeviceType().some((device) => DeviceTypeConst.RICH_DEVICES.includes(device));
  }

  /**
   * 判断是否包含瘦设备
   */
  public hasLiteDeviceInTarget(): boolean {
    return this.getTargetDeviceType().some((device) => DeviceTypeConst.LITE_DEVICES.includes(device));
  }

  /**
   * 判断是否是混合设备
   */
  public isMixedDeviceTarget(): boolean {
    return (
      this.getTargetDeviceType().some((device) => DeviceTypeConst.LITE_DEVICES.includes(device)) &&
      this.getTargetDeviceType().some((device) => DeviceTypeConst.RICH_DEVICES.includes(device))
    );
  }

  /**
   * 判断是否是纯富设备
   */
  public isPurerRichDeviceTarget(): boolean {
    return this.getTargetDeviceType().every((device) => DeviceTypeConst.RICH_DEVICES.includes(device));
  }

  /**
   * 判断是否是纯瘦设备
   */
  public isPurerLiteDeviceTarget(): boolean {
    return this.getTargetDeviceType().every((device) => DeviceTypeConst.LITE_DEVICES.includes(device));
  }

  /**
   * 判断是否是纯N设备
   */
  public isPurerNDeviceTarget(): boolean {
    const sysCapJsonPath = this._pathInfo.getSyscapJsonPathInfo(this._moduleModel);
    if (!fse.existsSync(sysCapJsonPath)) {
      return false;
    }
    const sysCapJson = getJson5Obj(sysCapJsonPath);
    const sysCapDevices = sysCapJson.devices;
    if (!sysCapDevices) {
      return false;
    }

    const sysCapGeneral = sysCapDevices.general;
    return sysCapGeneral === undefined || sysCapGeneral.length === 0;
  }

  /**
   *  返回设备类型的分类
   *
   *  @returns {[string]}
   */
  public getTargetDeviceTypeClasses(): Array<string> {
    const deviceTypeClasses: Array<string> = [];
    const deviceTypes = this.getTargetDeviceType();
    if (deviceTypes === undefined) {
      const apiType = this._moduleModel.getProfileOpt().apiType ?? ApiType.STAGE;
      const configFileName = apiType === ApiType.FA ? CommonConst.CONFIG_JSON : CommonConst.MODULE_JSON5;
      const configFilePath = path.resolve(this._moduleSourceSetModel.getSourceSetRoot(), configFileName);
      this._log.printErrorExit('DEVICETYPE_IS_NOT_CONFIGURED', [configFilePath]);
    }

    this.hasLiteDeviceInTarget() && deviceTypeClasses.push(DeviceTypeConst.LITE);
    this.hasRichDeviceInTarget() && deviceTypeClasses.push(DeviceTypeConst.RICH);
    this.isPurerNDeviceTarget() && deviceTypeClasses.push(DeviceTypeConst.RICH);

    return deviceTypeClasses;
  }

  /**
   * 判断是否是纯富/瘦设备类型
   *
   * @return {boolean}
   */
  public isSingleDeviceTypeTarget(): boolean {
    return this.isPurerRichDeviceTarget() || this.isPurerLiteDeviceTarget();
  }
}
